/**********************************************************************************************/
/* FBL_Atomic.h																				  */
/* ValentinaX																				  */
/*																							  */
/* Created by Shaun Wexler on 6/11/06.														  */
/* Copyright (c) 2006 SKW Development. All rights reserved.									  */
/**********************************************************************************************/

#ifndef _FBL_Atomic_h 
#define _FBL_Atomic_h
#pragma once

#ifndef _FBL_Macros_h
#include <VShared/FBL/publ/Headers/FBL_Macros.h>
#endif

/**********************************************************************************************/
#if FBL_THREADSAFE


/**********************************************************************************************/
FBL_Begin_Namespace


/**********************************************************************************************/
#if __cplusplus
extern "C" {
#endif
	
	
/**********************************************************************************************/
// 32-bit atomic types - int32 - cannnot be long!  64-bit computers will break!!!
//
typedef volatile UInt32 AtomicUInt32 FBL_ALIGNED(4);
typedef volatile SInt32 AtomicInt32  FBL_ALIGNED(4);


/**********************************************************************************************/
// atomic lock primitive, used for fast inline mutexing
//
typedef AtomicUInt32 AtomicLock;


/**********************************************************************************************/
// atomic read-write lock primitive, used for multiple-reader single-writer mutexing
//
typedef struct AtomicRWLock 
{
	AtomicLock		writer_lock;
	AtomicUInt32 	reader_count;
	
} AtomicRWLock;



#pragma mark -
#pragma mark ====== C code ======


/**********************************************************************************************/
// atomically add to a value in memory, returning the previous value from memory
//
FBL_INLINE(UInt32) AtomicExchangeAdd( AtomicUInt32 *memptr, UInt32 addend ) FBL_INLINE_ATTR;
FBL_INLINE(UInt32) AtomicExchangeAdd( AtomicUInt32 *memptr, UInt32 addend )
{
	register UInt32 oldval;
	asm volatile (
#if __ppc__
				  "	1:	lwarx   %0,0,%1		\n"	// load memval, taking a cacheline reservation
				  "		add		r0,%2,%0	\n"	// add with addend, preserving oldval
				  "		stwcx.  r0,0,%1		\n"	// conditionally store newval
				  "		bne-    1b			\n"	// retry if reservation failed
				  : "=&r" (oldval)
				  : "r" (memptr), "r" (addend)
				  : "cc", "memory", "r0"
#elif __i386__
				  "		lock				\n"	// force next operation to execute atomically
				  "		xaddl	%0,%1		\n"	// add to value in memory, returning oldval
				  : "=r" (oldval)
				  : "m" (*memptr), "0" (addend) 
				  : "memory"
#else
#error undefined arch
#endif
				  );
	return oldval;	
}


/**********************************************************************************************/
// atomically add to a value in memory, returning the result of the addition
//
FBL_INLINE(UInt32) AtomicAdd( AtomicUInt32 *memptr, UInt32 addend ) FBL_INLINE_ATTR;
FBL_INLINE(UInt32) AtomicAdd( AtomicUInt32 *memptr, UInt32 addend )
{
	register UInt32 newval;
	asm volatile (
#if __ppc__
				  "	1:	lwarx   %0,0,%1		\n"	// load memval, taking a cacheline reservation
				  "		add		%0,%0,%2	\n"	// add with addend, replacing newval
				  "		stwcx.  %0,0,%1		\n"	// conditionally store newval
				  "		bne-    1b			\n"	// retry if reservation failed
				  : "=&r" (newval)
				  : "r" (memptr), "r" (addend)
				  : "cc", "memory"
#elif __i386__
				  "		mov		%2,%0		\n"	// copy addend (i386 op's are destructve)
				  "		lock				\n"	// force next operation to execute atomically
				  "		xaddl	%0,%1		\n"	// add to value in memory, returning oldval
				  "		add		%2,%0		\n"	// add addend to oldval, to determine newval
				  : "=r" (newval)
				  : "m" (*memptr), "r" (addend)
				  : "memory"
#else
#error undefined arch
#endif
				  );
	return newval;	
}


/**********************************************************************************************/
// atomically read a value from memory, returning its current value - note: somewhat heuristic
//
FBL_INLINE(UInt32) AtomicGet( AtomicUInt32 *memptr ) FBL_INLINE_ATTR;
FBL_INLINE(UInt32) AtomicGet( AtomicUInt32 *memptr )
{
	register UInt32 curval;
	asm volatile (
#if __ppc__
				  "	1:	lwarx   %0,0,%1		\n"	// load memval, taking a cacheline reservation
				  "		stwcx.  %0,0,%1		\n"	// conditionally store same value
				  "		bne-    1b			\n"	// retry if reservation failed
				  : "=&r" (curval)
				  : "r" (memptr)
				  : "cc"
#elif __i386__
				  "		lock				\n"	// force next operation to execute atomically
				  "		xaddl	%0,%1		\n"	// add 0 to value in memory, returning value
				  : "=&r" (curval)
				  : "m" (*memptr), "0" (0)
#else
#error undefined arch
#endif
				  );
	return curval;
}
	
	
#if __cplusplus
} // extern "C"
#endif


/**********************************************************************************************/
FBL_End_Namespace


/**********************************************************************************************/
#endif // FBL_THREADSAFE


/**********************************************************************************************/
#endif // _FBL_Atomic_h
